package base

import (
	"reflect"
	"time"

	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"hub.hddznet.com/app/midway_app/utils"
	"hub.hddznet.com/app/midway_app/views"
	"hub.hddznet.com/app/midway/misc"
	"hub.hddznet.com/app/midway_app/errors"
	"hub.hddznet.com/uniplatform/framework/logging"
)

type MgoDatabase struct {
	*mgo.Database
}

//封装mgo的collection，对于增删改进行二次封装
type MgoCollection struct {
	*mgo.Collection
}

//可同时插入多个对象
func (this MgoCollection) HDInsert(objs ... interface{}) (error) {

	for _, o := range objs {
		ty := reflect.TypeOf(o)
		if ty.Kind() != reflect.Ptr || ty.Elem().Kind() != reflect.Struct {
			panic("注入对象必须为指针对象，并且指向的必须是struct")
		}
	}

	oos := make([]interface{}, 0)
	for _, o := range objs {
		this.hdCommon(o,bson.NewObjectId().Hex())
		oos = append(oos, o)
	}
	err := this.Insert(oos...)
	if err != nil {
		logging.Error("批量插入失败", err)
		return errors.MgoInsertErr(err)
	}

	return nil
}

func (this MgoCollection) HDUpdate(query interface{}, set interface{}) (error) {
	err := this.Update(query, bson.M{"$set": set})
	if err != nil {
		logging.Error("更新失败", err)
		return errors.MgoUpdateErr(err)
	}
	return nil
}

func (this MgoCollection) HDUpsertId(id interface{}, obj interface{}) (error) {
	this.hdCommon(obj,"")
	_, err := this.UpsertId(id, bson.M{"$set": obj})
	if err != nil {
		logging.Error("更新失败", err)
		return errors.MgoUpdateErr(err)
	}
	return nil
}

func (this MgoCollection) HDSave(objs ... interface{}) (err error) {
	for _, obj := range objs {
		id := this.hdCommon(obj,"")
		err := this.HDUpsertId(id, obj)
		if err != nil {
			logging.Error("批量保存失败", err)
			return err
		}
	}
	return nil
}

func (this MgoCollection) HDGetById(id string, obj interface{}) error {
	err := this.HDGetOne(&bson.M{"_id": id}, obj)
	if err != nil {
		logging.Error("查询出错", err)
		return errors.MgoSearchErr(err)
	}
	return nil
}

/*
	排序规则，例如：
	version:正序
	-version:反序

 */
func (this MgoCollection) HDGetOne(query interface{}, obj interface{}, sort ... string) error {

	ty := reflect.TypeOf(obj)
	if ty.Kind() != reflect.Ptr || ty.Elem().Kind() != reflect.Struct {
		panic("注入对象必须为指针对象，并且指向的必须是struct")
	}

	ty = reflect.SliceOf(ty.Elem())
	vl := reflect.New(ty)
	results := vl.Interface()
	err := this.Find(query).Sort(sort...).All(results)
	if err != nil {
		logging.Error("查询出错", err)
		return errors.MgoSearchErr(err)
	}

	if vl.Elem().Len() > 0 {
		reflect.ValueOf(obj).Elem().Set(vl.Elem().Index(0))
		return nil
	}
	return nil
}

func (this MgoCollection) HDGetAllAndSort(query interface{}, obj interface{}, sort ... string) error {
	ty := reflect.TypeOf(obj)
	if ty.Kind() != reflect.Ptr || ty.Elem().Kind() != reflect.Slice {
		panic("注入对象必须为指针对象，并且指向的必须是slice")
	}
	err := this.Find(query).Sort(sort...).All(obj)
	if err != nil {
		logging.Error("查询失败", err)
		return errors.MgoSearchErr(err)
	}
	return nil
}

func (this MgoCollection) HDGetAll(query interface{}, obj interface{}) error {
	ty := reflect.TypeOf(obj)
	if ty.Kind() != reflect.Ptr || ty.Elem().Kind() != reflect.Slice {
		panic("注入对象必须为指针对象，并且指向的必须是slice")
	}
	err := this.Find(query).All(obj)
	if err != nil {
		logging.Error("查询失败", err)
		return errors.MgoSearchErr(err)
	}
	return nil
}

func (this MgoCollection) HDAggregateAll(query interface{}, obj interface{}) error {
	ty := reflect.TypeOf(obj)
	if ty.Kind() != reflect.Ptr || ty.Elem().Kind() != reflect.Slice {
		panic("注入对象必须为指针对象，并且指向的必须是slice")
	}
	err := this.Pipe(query).All(obj)
	if err != nil {
		logging.Error("查询失败", err)
		return errors.MgoAggregateSearchErr(err)
	}
	return nil
}

func (this MgoCollection) HDItems(page *views.PageParam) (*views.PageResult, error) {
	result := new(views.PageResult)

	// 查询条件
	var query map[string]interface{}
	if page.Querys == nil {
		query = map[string]interface{}{}
	} else {
		query = page.Querys
	}
	mgoquery := this.Find(query)

	// 总条数
	total, err := mgoquery.Count()
	if err != nil {
		logging.Error("查询总数出错", err)
		return nil, errors.MgoSearchErr(err)
	}
	result.Total = misc.PtrOfInt(total)

	// 分页
	if page.Index != nil && page.Size != nil {
		mgoquery = mgoquery.Limit(*page.Size).Skip(*page.Size * (*page.Index - 1))
	}

	// 排序
	if page.Order != nil {
		orders := make([]string, 0)
		for _, o := range page.Order {
			if o.Column == nil {
				continue
			}
			ty := ""
			if o.Type {
				ty = "+"
			} else {
				ty = "-"
			}
			orders = append(orders, ty + *o.Column)
		}
		mgoquery.Sort(orders...)
	}

	// 读取返回值类型
	obj := page.ResultType
	if obj == nil {
		return nil, errors.MgoQueryParamErr()
	}

	// 查询数据
	its := reflect.New(reflect.TypeOf(obj)).Interface()
	err = mgoquery.All(its)
	if err != nil {
		logging.Error("查询总数出错", err)
		return nil, errors.MgoSearchErr(err)
	}
	if its != nil {
		ty := reflect.TypeOf(its)
		vl := reflect.ValueOf(its)
		if ty.Kind() == reflect.Ptr {
			vl = vl.Elem()
		}
		result.Rows = vl.Interface()
	}

	return result, nil
}

func (this MgoCollection) HDQuery(page *views.QueryParam,result interface{}) (interface{}, error) {
	// 查询条件
	var query map[string]interface{}
	if page.Querys == nil {
		query = map[string]interface{}{}
	} else {
		query = page.Querys
	}
	mgoquery := this.Find(query)

	// 排序
	if page.Order != nil {
		orders := make([]string, 0)
		for _, o := range page.Order {
			if o.Column == nil {
				continue
			}
			ty := ""
			if o.Type {
				ty = "+"
			} else {
				ty = "-"
			}
			orders = append(orders, ty + *o.Column)
		}
		mgoquery.Sort(orders...)
	}

	// 读取返回值类型
	if result == nil {
		return nil, errors.MgoQueryParamErr()
	}

	// 查询数据
	its := reflect.New(reflect.TypeOf(result)).Interface()
	err := mgoquery.All(its)
	if err != nil {
		logging.Error("查询总数出错", err)
		return nil, errors.MgoSearchErr(err)
	}
	return its, nil
}

// 此方法会返回一个ID，当对象中存在ID值的时候，返回的是对象中的ID，当对象中不存在ID值的时候，返回的
// 是一个新建的ID
func (this MgoCollection) hdCommon(obj interface{},id string) string {
	utils.RftStruct(obj, func(ty reflect.Type, vl reflect.Value, tag reflect.StructTag) {
		switch tag.Get("bson") {
		case "changed":
			if ty.Kind() == reflect.Ptr {
				if vl.Pointer() == 0 {
					vl.Set(reflect.New(ty.Elem()))
				}
				vl.Elem().Set(reflect.ValueOf(time.Now().Unix()))
			}
		case "created":
			if ty.Kind() == reflect.Ptr {
				if vl.Pointer() == 0 {
					vl.Set(reflect.New(ty.Elem()))
					vl.Elem().Set(reflect.ValueOf(time.Now().Unix()))
				}
			}
		case "_id":
			if ty.Kind() == reflect.Ptr {
				if vl.Pointer() == 0 {
					vl.Set(reflect.New(ty.Elem()))
					id = bson.NewObjectId().Hex()
					vl.Elem().Set(reflect.ValueOf(id))
				} else {
					if id == "" {
						id = vl.Elem().Interface().(string)
					}else{
						vl.Elem().Set(reflect.ValueOf(id))
					}
				}
			}

		}
	})
	return id
}
